#include <boost/algorithm/string/trim.hpp>
#include <boost/regex.hpp>
#include "HtmlReader.h"

using namespace kc_sf;

const std::map<HtmlReader::ErrorType, std::string> HtmlReader::ErrMsgs =
		HtmlReader::initErrMsgs();

const std::map<HtmlReader::ErrorType, std::string> HtmlReader::initErrMsgs() 
{
	std::map<ErrorType, std::string> err_msgs;

	err_msgs.insert(std::make_pair(SYNTAX_E, "Niepoprawny format"));
	err_msgs.insert(std::make_pair(NO_TAGS_E, "Nie istnieją żadne tagi"));
	err_msgs.insert(std::make_pair(BAD_CLOSE_TAG_E,
			"Tag otwierający zakończony nieodpowiednim tagiem zamykającym "));
	err_msgs.insert(std::make_pair(NO_OPEN_TAG_E, "Brak otwierającego tagu "));
	err_msgs.insert(std::make_pair(NO_CLOSE_TAG_E, "Brak zamykającego tagu "));
	err_msgs.insert(std::make_pair(NO_COMMENT_END_E, "Niezamknięty komentarz "));

	return err_msgs;
}

// ----------------------------------------------------------------------

HtmlReader::HtmlReader(const std::string &filepath, int _max_errors)
: hr_filename(filepath),
  hr_freader(filepath),
  hr_max_errors(_max_errors)
{
	if (hr_max_errors <= 0)
		hr_max_errors = 1;
}

// ----------------------------------------------------------------------

HtmlTree HtmlReader::read()
{
	unsigned int start = findNxtTagStart(), end = 0, eof = hr_freader.size();

	// nie było żadnych znaczników
	if (start == eof)
	{
		hr_errors.push_back(ElemError(NO_TAGS_E, ""));
		return hr_tree; // @todo zamiast return zliczanie bledow
	}

	while (start != eof && hr_errors.size() < hr_max_errors)
	{
		std::string tag;
		// >zawartość taga i/lub spacje<
		if (start > 0 && end < start - 1)
		{
			tag = hr_freader.read(end + 1, start);
			boost::trim(tag);
			tag = boost::regex_replace(tag, boost::regex("(\r)+|(\n)+"),
					std::string("")); // bez znaków nowej linii
			if (!tag.empty())
			{
				if (!hr_tree.addContent(tag))
				{
					hr_errors.push_back(ElemError(NO_OPEN_TAG_E, tag));
				}
			}
		}

		end = findNxtTagEnd(start);
		if (end == eof)
		{ // nie ma już ">"
			hr_errors.push_back(ElemError(SYNTAX_E, hr_freader.read(start, eof
					- start)));
			break;
		}

		tag = hr_freader.read(start + 1, end);
		boost::trim(tag);
		tag = boost::regex_replace(tag, boost::regex("(\r)+|(\n)+"),
				std::string("")); // bez znaków nowej linii
		if (tag.size() > 2 && tag[0] == '!' && tag[1] == '-' && tag[2] == '-')
		{ // komentarz <!-- -->
			unsigned int ce = findCommentEnd(start + 4);
			if (ce == eof)
			{
				// niedomkniety komentarz
				hr_errors.push_back(ElemError(NO_COMMENT_END_E, tag.substr(3,
						tag.size() - 3) + std::string("...")));
			}
			end = ce;
		} 
		else
		{ // znacznik
			if (tag[0] == '/')
			{ // </tag>
				if (tag.size() == 1)
				{
					hr_errors.push_back(ElemError(SYNTAX_E, tag));
				} 
				else
				{
					if (!hr_tree.addNode(HtmlTag(tag.substr(1, tag.size() - 1),
							HtmlTag::CLOSE_TAG)))
					{
						hr_errors.push_back(ElemError(NO_OPEN_TAG_E, tag));
					}
				}
			} 
			else if (tag[tag.size() - 1] == '/')
			{ // <tag/>
				if (!hr_tree.addNode(HtmlTag(tag.substr(0, tag.size() - 1),
						HtmlTag::NO_CONTENT_TAG)))
				{
					hr_errors.push_back(ElemError(BAD_CLOSE_TAG_E, tag));
				}
			} 
			else
			{ // <tag>
				hr_tree.addNode(HtmlTag(tag, HtmlTag::OPEN_TAG));
			}
		}
		start = findNxtTagStart(end);
	}

	HtmlTag err_tag("");
	bool too_many_errs = hr_errors.size() < hr_max_errors;
	if (!too_many_errs && !hr_tree.isFullTree(err_tag))
		hr_errors.push_back(ElemError(NO_CLOSE_TAG_E, err_tag.name()));

	if (hr_errors.size() >= 1)
	{
		std::cerr << "\tUwaga! ";
		if (hr_errors.size() == 1)
			std::cerr << "Błąd";
		else
			std::cerr << "Błędy";
		std::cerr << " w strukturze pliku HTML: \"" << hr_filename << "\". "
				<< std::endl;

		for (unsigned int i = 0; i < hr_errors.size(); ++i)
		{
			std::cerr << ErrMsgs.find(hr_errors[i].first)->second;
			if (!hr_errors[i].second.empty())
				std::cerr << "\"" << hr_errors[i].second << "\"." << std::endl;
			else
				std::cerr << std::endl;
		}

		!too_many_errs ? std::cerr << "Błędne tagi zostaną pominięte!\n"
				: std::cerr << "Przerywam odczyt pliku!\n";
	}

	return hr_tree;
}

// ----------------------------------------------------
unsigned int HtmlReader::findNxtTagStart(unsigned int starting_from)
{
	char c = ' ';
	unsigned int eof = hr_freader.size();
	while (c != '<' && starting_from < eof)
		c = hr_freader.charAt(starting_from++);

	return (c == '<') ? --starting_from : eof;
}

unsigned int HtmlReader::findNxtTagEnd(unsigned int starting_from)
{
	char c = ' ';
	unsigned int eof = hr_freader.size();
	while (c != '>' && starting_from < eof)
		c = hr_freader.charAt(starting_from++);

	return (c == '>') ? --starting_from : eof;
}

unsigned int HtmlReader::findCommentEnd(unsigned int starting_from)
{
	char c = ' ', nxt_c = ' ';
	unsigned int eof = hr_freader.size();

	while (starting_from < eof && (nxt_c != '-' || c != '-'))
	{
		c = nxt_c;
		nxt_c = hr_freader.charAt(starting_from++);
	}

	if (c == '-' && nxt_c == '-')
	{
		c = hr_freader.charAt(starting_from);
	}

	return (c == '>' && starting_from < eof) ? starting_from : eof;
}
